package service

import (
	"bytes"
	"cgs-server/helper"
	"cgs-server/server/cgs/app/models"
	"cgs-server/server/cgs/app/models/dto"
	v1 "cgs-server/server/cgs/app/models/v1"
	"cgs-server/server/cgs/app/models/vo"
	"cgs-server/server/cgs/app/store"
	"cgs-server/server/model"
	"errors"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"io"
	"mime/multipart"
	"net/url"
	"path/filepath"
	"strings"
)

type WorksheetPrintSrv interface {
	ListByWorksheetId(worksheetId string, user *model.User) (*vo.WorksheetPrintList, error)
	Detail(id string, user *model.User) (*vo.WorksheetPrint, error)
	Delete(id string, user *model.User) error
	Save(tempFile *multipart.FileHeader, wp *models.WorksheetPrint, user *model.User) error
	Update(tempFile *multipart.FileHeader, wp *models.WorksheetPrint, user *model.User) error
	Render(wp *dto.WorksheetPrint, user *model.User) (*vo.CarboneResult, error)
	GetFile(wf *dto.WorksheetPrintFile) (*vo.CarboneFileResult, error)
}

type worksheetPrintSrv struct {
	store store.Factory
}

func (w *worksheetPrintSrv) GetFile(wf *dto.WorksheetPrintFile) (*vo.CarboneFileResult, error) {
	var res = &vo.CarboneFileResult{}
	// 获取模板文件或者渲染后的文件
	if wf.TemplateId != "" {
		worksheetPrint, err := w.Detail(wf.TemplateId, nil)
		if err != nil {
			return nil, err
		}
		fileBytes, err := helper.Get("http://localhost:5000/" + worksheetPrint.Path)
		if err != nil {
			return nil, err
		}
		ext := filepath.Ext(worksheetPrint.FileName)
		res.Body = fileBytes
		res.Name = worksheetPrint.Name + ext
	} else {
		if wf.WorksheetId == "" || wf.RenderPath == "" {
			return nil, errors.New("worksheetId, rowId and renderPath is required")
		}
		objId, err := primitive.ObjectIDFromHex(wf.WorksheetId)
		if err != nil {
			return nil, err
		}
		info, err := w.store.WorksheetInfo().Get(&models.WorksheetInfo{Id: objId}, nil, v1.GetOptions{})
		if err != nil {
			return nil, err
		}
		fileBytes, err := helper.Get("http://localhost:5000/" + wf.RenderPath)
		if err != nil {
			return nil, err
		}
		ext := filepath.Ext(wf.RenderPath)
		res.Body = fileBytes
		res.Name = info.Name + "-record" + ext
	}
	return res, nil
}

func (w *worksheetPrintSrv) Render(wp *dto.WorksheetPrint, user *model.User) (*vo.CarboneResult, error) {
	// 查询模板信息
	worksheetPrint, err := w.Detail(wp.GetId(), user)
	if err != nil {
		return nil, err
	}
	var data map[string]interface{}
	if wp.RowId != "" {
		// 查询一条表单记录
		row, err := w.store.WorksheetRows().Get(&models.WorksheetRow{RowId: wp.RowId}, user, v1.GetOptions{})
		if err != nil {
			return nil, err
		}
		objId, err := primitive.ObjectIDFromHex(wp.WorksheetId)
		if err != nil {
			return nil, err
		}
		info, err := w.store.WorksheetInfo().Get(&models.WorksheetInfo{Id: objId}, user, v1.GetOptions{})
		if err != nil {
			return nil, err
		}
		data, err = row.Data.FormatData(info.Fields)
		if err != nil {
			return nil, err
		}

	} else if wp.RenderData != nil {
		data = wp.RenderData
	}

	param, err := helper.ToJSON(bson.M{
		"tempPath": worksheetPrint.Path,
		"data":     data,
	})
	if err != nil {
		return nil, err
	}

	body, err := helper.Request("http://localhost:5000/template/render", bson.M{
		"method": "POST",
		"headers": bson.M{
			"Content-Type": "application/json",
		},
		"body": bytes.NewReader(param),
	})
	var result vo.CarboneResult
	if err := helper.FromJSON(body, &result); err != nil {
		return nil, err
	}
	if !result.Success {
		return nil, errors.New(result.Msg)
	}

	return &result, nil
}

func (w *worksheetPrintSrv) UploadTemplateFile(tempFile *multipart.FileHeader) (*vo.CarboneResult, error) {
	// 上传模板文件
	bodyBuf := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(bodyBuf)
	file, err := tempFile.Open()
	if err != nil {
		return nil, err
	}
	defer file.Close()
	formFileWriter, err := bodyWriter.CreateFormFile("template", tempFile.Filename)
	if _, err := io.Copy(formFileWriter, file); err != nil {
		return nil, err
	}
	contentType := bodyWriter.FormDataContentType()
	err = bodyWriter.Close()
	if err != nil {
		return nil, err
	}
	body, err := helper.Request("http://localhost:5000/template/add", bson.M{
		"method": "POST",
		"headers": bson.M{
			"Content-Type": contentType,
		},
		"body": bodyBuf,
	})
	if err != nil {
		return nil, err
	}
	doc := vo.CarboneResult{}
	if err := helper.FromJSON(body, &doc); err != nil {
		return nil, err
	}
	if !doc.Success {
		return nil, errors.New("upload template file failed")
	}
	return &doc, nil
}

func (w *worksheetPrintSrv) Update(tempFile *multipart.FileHeader, wp *models.WorksheetPrint, user *model.User) error {
	if tempFile != nil {
		ext := filepath.Ext(tempFile.Filename)
		typ := -1
		switch ext {
		case ".docx", ".odt":
			typ = 1
		}
		if typ == -1 {
			return errors.New("unsupported file type")
		}

		doc, err := w.UploadTemplateFile(tempFile)
		if err != nil {
			return err
		}

		// 删除模板文件
		worksheetPrint, err := w.Detail(wp.GetId(), user)
		data := url.Values{}
		data.Set("path", worksheetPrint.Path)
		_, _ = helper.Post("http://localhost:5000/template/delete", data)

		wp.FileName = tempFile.Filename
		wp.Path = doc.Data["path"].(string)
	}
	err := w.store.WorksheetPrint().Update(wp, user, v1.UpdateOptions{})
	return err
}

func (w *worksheetPrintSrv) Save(tempFile *multipart.FileHeader, wp *models.WorksheetPrint, user *model.User) error {
	ext := filepath.Ext(tempFile.Filename)
	typ := -1
	switch ext {
	case ".docx", ".odt":
		typ = 1
	}
	if typ == -1 {
		return errors.New("unsupported file type")
	}

	doc, err := w.UploadTemplateFile(tempFile)
	if err != nil {
		return err
	}

	name := strings.TrimSuffix(tempFile.Filename, ext)

	wp.Name = name
	wp.FileName = tempFile.Filename
	wp.Type = typ
	wp.Path = doc.Data["path"].(string)

	err = w.store.WorksheetPrint().Save(wp, user, v1.CreateOptions{})
	if err != nil {
		return err
	}
	return nil
}

func (w *worksheetPrintSrv) Delete(id string, user *model.User) error {
	objId, err := primitive.ObjectIDFromHex(id)
	if err != nil {
		return err
	}

	worksheetPrint, err := w.Detail(id, user)
	// 删除模板文件
	data := url.Values{}
	data.Set("path", worksheetPrint.Path)
	body, err := helper.Post("http://localhost:5000/template/delete", data)
	if err != nil {
		return err
	}
	doc := vo.CarboneResult{}
	if err := helper.FromJSON(body, &doc); err != nil {
		return err
	}
	if !doc.Success {
		return errors.New("delete template file error")
	}

	err = w.store.WorksheetPrint().Delete(&models.WorksheetPrint{Id: objId}, user, v1.DeleteOptions{})
	return err
}

func (w *worksheetPrintSrv) Detail(id string, user *model.User) (*vo.WorksheetPrint, error) {
	objId, err := primitive.ObjectIDFromHex(id)
	if err != nil {
		return nil, err
	}
	wg := models.WorksheetPrint{Id: objId}
	worksheetPrint, err := w.store.WorksheetPrint().Get(&wg, user, v1.GetOptions{})
	if err != nil {
		return nil, err
	}

	return worksheetPrint, err
}

func (w *worksheetPrintSrv) ListByWorksheetId(worksheetId string, user *model.User) (*vo.WorksheetPrintList, error) {
	wg := models.WorksheetPrint{}
	opts := v1.ListOptions{}
	opts.AddFilter("worksheetId", worksheetId)
	list, err := w.store.WorksheetPrint().List(&wg, user, opts)
	return list, err
}

func newWorksheetPrintSrv(s *service) WorksheetPrintSrv {
	return &worksheetPrintSrv{
		store: s.store,
	}
}
